/**
 * 使用 TS 对 Axios 进行二次封装
 *
 * 受到 TS 对类型检查的启发，发现 Axios 其实对泛型约束支持的挺好。
 * 所以重新设计 request 的实现方式。
 *
 * author: zengsun
 * date: 2022-1-29
 */

import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from "axios";
import { notEmpty, stringify } from "./utils";
import createEmitter from "./emit";

interface RequestConfig<T = any> extends AxiosRequestConfig<T> {
  ignoreNotifyError?: boolean;
}

interface HttpError {
  status: number;
  message: string;
  data: any;
}

const event = createEmitter<{
    onRequest: AxiosRequestConfig;
    onResponse: AxiosResponse;
    onError: HttpError;
  }>(),
  errorMessage: Record<number, string> = {
    0: "无法连接服务",
    403: "资源拒绝访问",
    401: "未验证的用户",
    504: "代理访问错误",
    500: "服务运行错误",
    404: "服务资源不存在",
    302: "登录过期",
  };

const createRequest = (options: RequestConfig) => {
  // AxiosRequestInstance
  const http = axios.create(options);
  // 拦截器
  http.interceptors.request.use((config) => {
    event.emit("onRequest", config);
    return config;
  });
  http.interceptors.response.use(
    (response) => {
      event.emit("onResponse", response);
      return response;
    },
    (error) => {
      const err = error as AxiosError;
      // if ((err.config as RequestConfig<any>).ignoreNotifyError) {
      //   return Promise.reject(error);
      // }

      if (err.response) {
        const { status, data } = err.response;
        if (status in errorMessage)
          event.emit("onError", {
            status,
            message: errorMessage[status],
            data,
          });
        return Promise.resolve(err.response);
      } else if (err.code == "ECONNABORTED") {
        event.emit("onError", {
          status: 0,
          message: errorMessage[0],
          data: {},
        });
      }
      return Promise.reject(error);
    }
  );
  return <T>(url: string, options: RequestConfig = {}) => {
    const { headers = {}, method = "GET", data = {} } = options;
    // process data
    if (
      headers["Content-Type"] === "application/x-www-form-urlencoded" &&
      notEmpty(data)
    ) {
      options.data = stringify(data);
    }
    Object.assign(options, { url, method });
    return http.request<T>(options);
  };
};

export { RequestConfig, createRequest, event };

export default createRequest;
